package com.wx.rabbitmq.config;

import com.wx.rabbitmq.convert.ImageMessageConverter;
import com.wx.rabbitmq.convert.PDFMessageConverter;
import com.wx.rabbitmq.convert.TextMessageConverter;
import com.wx.rabbitmq.msgadapter.MessageAdapter;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.amqp.support.ConsumerTagStrategy;
import org.springframework.amqp.support.converter.ContentTypeDelegatingMessageConverter;
import org.springframework.amqp.support.converter.DefaultJackson2JavaTypeMapper;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @author wx
 * @Description: rabbitMQ配置类
 * @date 2018/11/24 002418:47
 */
@Configuration
public class RabbitMQConfig {
    public final static String TOPIC_1 = "topic001";
    public final static String TOPIC_2 = "topic002";
    public final static String QUEUE_1 = "queue001";
    public final static String QUEUE_2 = "queue002";
    public final static String QUEUE_3 = "queue003";
    public final static String IMAGE_QUEUE = "image_queue";
    public final static String PDF_QUEUE = "pdf_queue";
    public final static String ROUTING_KEY_SPRING = "spring.*";
    public final static String ROUTING_KEY_RABBIT = "rabbit.*";
    public final static String ROUTING_KEY_MQ = "mq.*";

    /**
     * @param
     * @description: 注入mq连接工厂到ioc
     * @author: wx
     * @date: 2018/11/24 0024
     * @return: org.springframework.amqp.rabbit.connection.ConnectionFactory
     */
    @Bean
    public ConnectionFactory connectionFactory() {
        CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
        cachingConnectionFactory.setAddresses("localhost:5672");
        cachingConnectionFactory.setUsername("guest");
        cachingConnectionFactory.setPassword("guest");
        cachingConnectionFactory.setVirtualHost("/");
        return cachingConnectionFactory;
    }


    /**
     * @param connectionFactory
     * @description: 注入rabbitAdmin到ioc（RabbitAdmin底层是基于RabbitTemplate实现的）
     * @author: wx
     * @date: 2018/11/24 0024
     * @return: org.springframework.amqp.rabbit.core.RabbitAdmin
     */
    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
        RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
        //autoStartup必须为true 否则无法注入容器
        rabbitAdmin.setAutoStartup(true);
        return rabbitAdmin;
    }


    /**
     * 声明式创建交换机、队列、绑定
     */
    @Bean
    public TopicExchange exchange001() {
        return new TopicExchange(TOPIC_1, true, false);
    }

    @Bean
    public Queue queue001() {
        return new Queue(QUEUE_1, true);
    }

    @Bean
    public Binding binding001() {
        return BindingBuilder.bind(queue001()).to(exchange001()).with(ROUTING_KEY_SPRING);
    }

    @Bean
    public TopicExchange exchange002() {
        return new TopicExchange(TOPIC_2, true, false);
    }

    @Bean
    public Queue queue002() {
        return new Queue(QUEUE_2, true);
    }

    @Bean
    public Binding binding002() {
        return BindingBuilder.bind(queue002()).to(exchange002()).with(ROUTING_KEY_RABBIT);
    }

    @Bean
    public Queue queue003() {
        return new Queue(QUEUE_3, true);
    }

    @Bean
    public Binding binding003() {
        return BindingBuilder.bind(queue003()).to(exchange001()).with(ROUTING_KEY_MQ);
    }

    @Bean
    public Queue queue_image() {
        return new Queue(IMAGE_QUEUE, true);
    }

    @Bean
    public Queue queue_pdf() {
        return new Queue(PDF_QUEUE, true);
    }


    /**
     * @param connectionFactory
     * @description: 注入rabbitTemplate模板类到ioc
     * @author: wx
     * @date: 2018/11/24 0024
     * @return: org.springframework.amqp.rabbit.core.RabbitTemplate
     */
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        return rabbitTemplate;
    }


    /**
     * @param connectionFactory
     * @description: 消息监听容器(消费者使用手段)
     * @author: wx
     * @date: 2018/11/24 0024
     * @return: org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer
     */
    @Bean
    public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) {

        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        //设置监听队列
        container.setQueues(queue001(), queue002(), queue003(), queue_image(), queue_pdf());
        //设置最小消费者数量
        container.setConcurrentConsumers(1);
        //设置最大消费者数量
        container.setMaxConcurrentConsumers(5);
        //是否重回队列 一般都是false
        container.setDefaultRequeueRejected(false);
        //是否自动签收消息 这里暂时设置为true 一般工作中是手动签收false
        container.setAcknowledgeMode(AcknowledgeMode.AUTO);
        //是否暴露channel的listener给已经注册的ChannelAwareMessageListener？
        container.setExposeListenerChannel(true);
        //设置消费端标签策略，自定义消费者标签
        container.setConsumerTagStrategy(new ConsumerTagStrategy() {
            @Override
            public String createConsumerTag(String queue) {
                return queue + "_" + UUID.randomUUID().toString();
            }
        });


        /** Start 监听消息 方式-1 设置内置的消息监听ChannelAwareMessageListener */
        /*container.setMessageListener(new ChannelAwareMessageListener() {
            @Override
            public void onMessage(Message message, Channel channel) throws Exception {
                String msg = new String(message.getBody());
                System.err.println("----------消费者: " + msg);
            }
        });*/
        /** End  */

        /** Start 监听消息 方式-2.1 适配器方式:默认的消息处理方法名字的：handleMessage */
         /*MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageAdapter());
         // 可以自己指定一个方法的名字: consumeMessage
         adapter.setDefaultListenerMethod("consumeMessage");
         //添加一个转换器: 从字节数组转换为String
         adapter.setMessageConverter(new TextMessageConverter());
         container.setMessageListener(adapter);*/
        /** End  */


        /** Start 监听消息 方式-2.2 适配器方式: 我们的队列名称 和 方法名称 也可以进行一一的匹配 */
         /*MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageAdapter());
         adapter.setMessageConverter(new TextMessageConverter());
         Map<String, String> queueOrTagToMethodName = new HashMap<>();
         //queue001的队列会适配到MessageAdapter-method1方法处理
         queueOrTagToMethodName.put("queue001", "method1");
        //queue002的队列会适配到MessageAdapter-method2方法处理
         queueOrTagToMethodName.put("queue002", "method2");
         adapter.setQueueOrTagToMethodName(queueOrTagToMethodName);
         container.setMessageListener(adapter);*/
        /** End  */

        /** Start 监听消息 方式-2.2 消息转换器: 设置json格式的转换器返回json数据(map) */
         /*MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageAdapter());
         adapter.setDefaultListenerMethod("consumeMessage");
         Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
         adapter.setMessageConverter(jackson2JsonMessageConverter);
         container.setMessageListener(adapter);*/
        /** End  */

        /** Start 监听消息 方式-2.2 消息转换器: 设置对象转换器，返回java对象 */
        /*MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageAdapter());
        adapter.setDefaultListenerMethod("consumeMessage");
        //消息字节先转换为json
        Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
        DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();
        //SpringBoot 2.0.0版本时
        //DefaultJackson2JavaTypeMapper类源码构造函数进行了修改，不再信任全部package而是仅仅信任java.util、java.lang
        //在把消息转换为实体对象的时候需要信任下包权限，否则会转换失败
        javaTypeMapper.setTrustedPackages("*");
        //设置DefaultJackson2JavaTypeMapper json转换为java对象 底层默认会取消息扩展属性__TypeId__ 设置的包名进行转换
        //发送消息的时候进行设置消息扩展属性__TypeId__
        jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper);
        adapter.setMessageConverter(jackson2JsonMessageConverter);
        container.setMessageListener(adapter);*/
        /** End  */

        /** Start 监听消息 方式-2.2 消息转换器: 设置java对象多映射转换器，返回java对象 */
        /*MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageAdapter());
        adapter.setDefaultListenerMethod("consumeMessage");
        Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
        DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();
        Map<String, Class<?>> idClassMapping = new HashMap<String, Class<?>>();
        //底层默认会取消息扩展属性__TypeId__ 的value作为key可以来查找idClassMappingm对应的value ,进行对象转换
        //发送消息的时候进行设置消息扩展属性__TypeId__
        idClassMapping.put("order", com.wx.rabbitmq.entity.Order.class);
        idClassMapping.put("packaged", com.wx.rabbitmq.entity.Packaged.class);
        javaTypeMapper.setIdClassMapping(idClassMapping);
        jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper);
        adapter.setMessageConverter(jackson2JsonMessageConverter);
        container.setMessageListener(adapter);*/
        /** End  */

        /** Start 监听消息 方式-2.2 消息转换器: 设置全局转换器 */
        MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageAdapter());
        adapter.setDefaultListenerMethod("consumeMessage");
        ContentTypeDelegatingMessageConverter convert = new ContentTypeDelegatingMessageConverter();
        TextMessageConverter textConvert = new TextMessageConverter();
        //消息类型对应指定的消息转换器
        convert.addDelegate("text", textConvert);
        convert.addDelegate("html/text", textConvert);
        convert.addDelegate("xml/text", textConvert);
        convert.addDelegate("text/plain", textConvert);

        Jackson2JsonMessageConverter jsonConvert = new Jackson2JsonMessageConverter();
        convert.addDelegate("json", jsonConvert);
        convert.addDelegate("application/json", jsonConvert);

        ImageMessageConverter imageConverter = new ImageMessageConverter();
        convert.addDelegate("image/png", imageConverter);
        convert.addDelegate("image", imageConverter);

        PDFMessageConverter pdfConverter = new PDFMessageConverter();
        convert.addDelegate("application/pdf", pdfConverter);


        adapter.setMessageConverter(convert);
        container.setMessageListener(adapter);
        /** End  */

        return container;

    }

}
